package ar.uba.fi.tonyvaliente.tree;

import java.util.Iterator;
import java.util.List;

import ar.uba.fi.tonyvaliente.files.File;
import ar.uba.fi.tonyvaliente.files.FileBlock;
import ar.uba.fi.tonyvaliente.files.FileRecord;

public class FileBPlusTreeInspector<K extends Comparable<K>, V> {
	private File file;
	private FileBPlusTreeNodePersistor<K, V> nodePersistor;
	
	public void inspect(FileBPlusTree<K, V> tree)
	{
		this.file = tree.getFile(); 
		this.nodePersistor = new FileBPlusTreeNodePersistor<K, V>(tree);

		try {
			showFileInfo();
			showFileRecordsInfo();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void showFileInfo() throws Exception {
		System.out.println("*** FILE INFORMATION ***");
		System.out.println();
		System.out.println("Block size...................: " + file.getBlockSize());
		System.out.println("Block count..................: " + file.getBlockCount());
		System.out.println("Root node reference position.: " + file.getProperty("RootNodeReference:Block") + "," + file.getProperty("RootNodeReference:Record"));
		System.out.println("Elements per node............: " + file.getProperty("ElementsPerNode"));
		System.out.println();
		System.out.println();
	}
	
	private void showFileRecordsInfo() throws Exception {
		System.out.println("*** RECORDS INFORMATION ***");
		System.out.println();
		
		for (int i = 0; i < file.getBlockCount(); i++) {
			FileBlock block = file.getBlock(i);
			System.out.printf("Block %d - Free: %d\n", i, block.getFreeBytes());
			for (int j = 0; j < block.getRecordCount(); j++) {
				FileRecord record = block.getRecord(j);
				showFileRecordInfo(i, j, record.getData());
			}
		}
	}
	
	private void showFileRecordInfo(int block, int offset, byte[] data)	throws Exception {
		BPlusTreeNode<K, V> node = this.nodePersistor.deserialize(data);
		InnerBPlusTreeNode<K, V> innerNode = null;
		LeafBPlusTreeNode<K, V> leafNode = null;
		
		if (node.isInner()) {
			innerNode = (InnerBPlusTreeNode<K, V>)node;
		} else {
			leafNode = (LeafBPlusTreeNode<K, V>)node;
		}

		int elementsLength = 0;
		String nodeKind = null;
		if (node.isInner()) {
			elementsLength = innerNode.getKeys().size();
			nodeKind = "I";
		} else {
			elementsLength = leafNode.getData().size();
			nodeKind = "L";
		}
		
		System.out.printf("Node %d,%d: (%d bytes, %d elem, [%s]) ", block, offset, data.length, elementsLength, nodeKind);
		
		FileBPlusTreeNodeReference<K, V> parentRef = (FileBPlusTreeNodeReference<K, V>)node.getParentReference();
		if (parentRef != null) {
			System.out.printf("[Parent = %d,%d] ", parentRef.getBlock(), parentRef.getRecord());
		} else {
			System.out.print("[Parent = null] ");
		}
		
		System.out.print("{");
		Iterator<K> it1 = null;
		if (node.isInner()) {
			it1 = innerNode.getKeys().iterator();
		} else {
			it1 = leafNode.getData().keySet().iterator();
		}
		while (it1.hasNext()) {
			K key = it1.next();
			System.out.print(key);
			if (it1.hasNext()) {
				System.out.print(',');
			}
		}
		System.out.print("}");
		
		if (node.isInner()) {
			List<BPlusTreeNodeReference<K, V>> references = innerNode.getReferences();

			System.out.print(" [Children: ");
			Iterator<BPlusTreeNodeReference<K, V>> it2 = references.iterator();
			while (it2.hasNext()) {
				BPlusTreeNodeReference<K, V> ref = it2.next();
				if (ref != null) {
					FileBPlusTreeNodeReference<K, V> fileRef = (FileBPlusTreeNodeReference<K, V>)ref;
					System.out.printf("(%d,%d)", fileRef.getBlock(), fileRef.getRecord());
				} else {
					System.out.print("null");
				}
				if (it2.hasNext()) {
					System.out.print(',');
				}
			}
			System.out.println("]");
		} else {
			System.out.print(" [Siblings: ");
			BPlusTreeNodeReference<K, V> leftRef = leafNode.getPreviousReference();
			if (leftRef != null) {
				FileBPlusTreeNodeReference<K, V> fileRef = (FileBPlusTreeNodeReference<K, V>)leftRef;
				System.out.printf("(%d,%d)", fileRef.getBlock(), fileRef.getRecord());
			} else
				System.out.print("null");
			System.out.print(',');
			BPlusTreeNodeReference<K, V> rightRef = leafNode.getNextReference();
			if (rightRef != null) {
				FileBPlusTreeNodeReference<K, V> fileRef = (FileBPlusTreeNodeReference<K, V>)rightRef;
				System.out.printf("(%d,%d)", fileRef.getBlock(), fileRef.getRecord());
			} else
				System.out.print("null");
			System.out.println("]");
		}
		System.out.flush();
	}
}
